package com.zhang.boot.web.config;

import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import com.zhang.boot.core.util.JsonUtil;
import com.zhang.boot.core.vo.request.RequestInfo;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

@Aspect
@Slf4j
@Component
public class AopConfig {
    /**
     * 定义日志切面
     */
    @Pointcut("execution(* com.zhang.boot.web.controller..*(..)))")
    private void logPointCut() {
    }

    @Around("logPointCut()")
    public Object logPrint(ProceedingJoinPoint point) throws Throwable {
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        if (sra == null) {
            return point.proceed();
        }
        HttpServletRequest request = sra.getRequest();
        RequestInfo requestInfo = RequestInfo.builder()
                .requestTime(DateUtil.formatLocalDateTime(LocalDateTime.now()))
                .uri(request.getRequestURI())
                .requestParam(getRequestParam(point, request))
                //.requestHeader(getHeader(request))
                .build();
        log.info(requestInfo.toString());
        // result的值就是被拦截方法的返回值
        long startTime = System.currentTimeMillis();
        Object result = point.proceed();
        long endTime = System.currentTimeMillis();
        requestInfo.setResponse(JsonUtil.bean2Json(result));
        requestInfo.setTotalTime(endTime - startTime);
        log.info(requestInfo.toString());
        return result;
    }

    private String getHeader(HttpServletRequest request) {
        Enumeration<String> headerNames = request.getHeaderNames();
        Map<String, Object> headerMap = new HashMap<>();
        while(headerNames.hasMoreElements()) {
            String element = headerNames.nextElement();
            String value = request.getHeader(element);
            headerMap.put(element, value);
        }
        return JsonUtil.bean2Json(headerMap);
    }

    private String getRequestParam(ProceedingJoinPoint point, HttpServletRequest request) {
        String params = "";
        Object[] args = point.getArgs();
        String method = request.getMethod();
        String queryString = request.getQueryString();
        //获取请求参数集合并进行遍历拼接
        if (args.length > 0) {
            if ("POST".equals(method)) {
                Object[] arguments = new Object[args.length];
                for (int i = 0; i < args.length; i++) {
                    if (args[i] instanceof ServletRequest || args[i] instanceof ServletResponse || args[i] instanceof MultipartFile) {
                        //ServletRequest不能序列化，从入参里排除，否则报异常：java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
                        //ServletResponse不能序列化 从入参里排除，否则报异常：java.lang.IllegalStateException: getOutputStream() has already been called for this response
                        continue;
                    }
                    arguments[i] = args[i];
                }
                try {
                    params = JSONUtil.toJsonStr(arguments);
                } catch (Exception e) {
                    params = Arrays.toString(arguments);
                }
            } else if ("GET".equals(method)) {
                params = queryString;
            }
        }
        return params;
    }

}
